#include "pcapio.h"
#include <QThread>
#include <pcap.h>
#include <string.h>
#include <QMutex>
#include <QDebug>
#include <QObject>
namespace PCAPIO{

	//全局缓冲区
	QVector<tag_packages> global_buffer;
	//全局接收位置
	QAtomicInteger<quint64> pcap_recv_pos;
	//停止标志
	std::atomic<bool> pcap_stop;

	//端口分配映射
	QMap<quint64,tag_portAssign> pcap_ports;
	//端口分配映射的互斥锁
	QMutex mtx_ports;
	//获取接口地址
	std::string ifaddresses(pcap_if_t *d);

	//获取接口列表
	QString pcapio_interfaces(QMap<QString,QString> & devmap)
	{
		QString res;
		pcap_if_t *alldevs;
		char errbuf[PCAP_ERRBUF_SIZE];
		if(pcap_findalldevs(&alldevs, errbuf) == -1){
			res = errbuf;
			return res;
		}
		for(auto d = alldevs; d != NULL; d = d->next){

			QString d_name = QString::fromLocal8Bit(d->name);
			QString d_des = QString::fromLocal8Bit(ifaddresses(d).c_str());
			devmap[d_name] = d_des;
		}

		pcap_freealldevs(alldevs);
		return res;
	}
	//打印地址
	std::string address_print(unsigned char * v, bool hex)
	{
		std::string res;
		char buf[1024];
		if (hex)
		{
			int left_zeros = 0, right_zeros = 0;
			while (left_zeros <sizeof(sockaddr::sa_data)&&!v[left_zeros] )
				++left_zeros;
			while (right_zeros <sizeof(sockaddr::sa_data)&&!v[sizeof(sockaddr::sa_data) - 1 - right_zeros] )
				++right_zeros;
			int left_begin = 0, right_end = sizeof(sockaddr::sa_data);
			std::string prefix, tailfix;
			if (left_zeros >0 && left_zeros>= right_zeros)
			{
				left_begin = left_zeros;
				prefix = ":";
			}
			else if (left_zeros < right_zeros)
			{
				right_end = sizeof(sockaddr::sa_data) - 1 - right_zeros + 1;
				tailfix = ":";
			}
			for (unsigned char i =left_begin; i<right_end;++i)
			{
				if (i-left_begin)   res += ":";
				snprintf(buf,1024,"%02X", (unsigned int)v[i]);
				res += buf;
			}
			res = prefix + res + tailfix;
		}
		else
		{
			int left_begin = 0, right_end = sizeof(sockaddr::sa_data);
			while (!v[left_begin] && left_begin+4 < right_end)
				++left_begin;
			while (!v[right_end-1] && left_begin+4 < right_end)
				--right_end;
			for (unsigned char i =left_begin; i<right_end;++i)
			{
				if (i-left_begin)   res += ".";
				snprintf(buf,1024,"%u", (unsigned int)v[i] );
				res += buf;
			}

		}
		return res;
	}
	/* Print all the available information on the given interface */
	std::string ifaddresses(pcap_if_t *d)
	{
		// 定义一个缓冲区，用于存储地址信息
		char buf[1024];
		// 定义一个指针，用于遍历地址信息
		pcap_addr_t *a;
		// 定义一个字符串，用于存储最终结果
		std::string res = d->name;
		// 添加换行符
		res += "\n";
		// 如果有描述信息，则添加到结果中
		if(d->description)
		{
			res += d->description;
			res += "\n";
		}

		// 遍历地址信息
		for(a=d->addresses;a;a=a->next) {
			// 将地址族转换为字符串，并添加到结果中
			snprintf(buf,1024,"{",(unsigned int)a->addr->sa_family);
			res += buf;
			// 判断地址族是否为IPv4
			bool hex = a->addr->sa_family==0x02?false:true;
			// 如果有地址信息，则添加到结果中
			if (a->addr)
			{
				snprintf(buf,1024,"%s",address_print((unsigned char *)a->addr->sa_data,hex).c_str());
				res += buf;
			}
			// 如果有子网掩码信息，则添加到结果中
			if (a->netmask)
			{
				snprintf(buf,1024,"/%s",address_print((unsigned char *)a->netmask->sa_data,hex).c_str());
				if (!hex)
					res += buf;
			}
			// 如果有广播地址信息，则添加到结果中
			if (a->broadaddr)
			{
				snprintf(buf,1024,"\n    Cast:%s ",address_print((unsigned char *)a->broadaddr->sa_data,hex).c_str());
				//res += buf;
			}
			// 如果有目标地址信息，则添加到结果中
			if (a->dstaddr)
			{
				snprintf(buf,1024,"\n    Dest:%s ",address_print((unsigned char *)a->dstaddr->sa_data,hex).c_str());
				//res += buf;
			}
			// 添加换行符
			res += "}\n";
		}
		// 返回最终结果
		return res;
	}

	//初始化缓冲区
	void init_buffer()
	{
		global_buffer.clear();
		for (int i = 0;i < PCAPIO_BUFCNT;++i)
		{
			tag_packages pack;
			pack.from_id = 0;
			pack.to_id = -1;
			pack.len = 0;
			pack.data.resize(PCAPIO_MAXPACK);
			global_buffer.push_back(pack);
		}
	}

	//重置
	void reset()
	{
		pcap_ports.clear();
		pcap_stop = false;
		pcap_recv_pos = 0;
		if (global_buffer.size()!=PCAPIO_BUFCNT)
			init_buffer();
	}

	//2. Run Cap Thread on interface.
	void recv_loop(QString itstr, QString filterStr, int id, int tcp_exlude,std::function<void (QString) > msg)
	{
		//定义一个变量，用于记录接收到的数据包总数
		quint64 total_caps = 0;
		//循环接收数据包，直到pcap_stop为true
		while (!pcap_stop)
		{
			//将QString类型的itstr转换为std::string类型
			std::string strDev = itstr.toStdString();

			pcap_t *handle = NULL;
			char errbuf[PCAP_ERRBUF_SIZE];
			memset(errbuf,0,sizeof(errbuf));
			//handle = pcap_open_live(strDev.c_str(), 65535, 1, 10, errbuf);
			//打开网络设备，获取句柄
			handle = pcap_create(strDev.c_str(), errbuf);

			if(handle == NULL)
			{
				//将错误信息转换为QString类型
				QString strErrL = QString::fromLocal8Bit(QByteArray(errbuf));
				//调用msg函数，将错误信息发送出去
				msg(QObject::tr("RECV pcap_open_live failed . %1").arg(strErrL));
				break;
			}
			//注意，要设置较大的缓存
			if (pcap_set_buffer_size(handle,128*1024*1024)!=0)
			{
				//调用msg函数，将错误信息发送出去
				msg(QObject::tr("Couldn't set buffer size: %1").arg(pcap_geterr(handle)));
				//pcap_close(handle);
				//break;
			}
			if (pcap_set_promisc(handle, 1)!=0)
			{
				//调用msg函数，将错误信息发送出去
				msg(QObject::tr("Couldn't pcap_set_promisc: %1").arg(pcap_geterr(handle)));
				//pcap_close(handle);
				//break;
			}
			if (pcap_set_snaplen(handle, 65536)!=0)
			{
				//调用msg函数，将错误信息发送出去
				msg(QObject::tr("Couldn't pcap_set_snaplen: %1").arg(pcap_geterr(handle)));
				//pcap_close(handle);
				//break;
			}
			if (pcap_set_timeout(handle, 1000)!=0)
			{
				//调用msg函数，将错误信息发送出去
				msg(QObject::tr("Couldn't pcap_set_timeout: %1").arg(pcap_geterr(handle)));
				//pcap_close(handle);
				//break;
			}
			if (pcap_activate(handle)!=0)
			{
				//调用msg函数，将错误信息发送出去
				msg(QObject::tr("Couldn't pcap_activate: %1").arg(pcap_geterr(handle)));
				pcap_close(handle);
				break;
			}
			if (pcap_datalink(handle)!= DLT_EN10MB)
			{
				//调用msg函数，将错误信息发送出去
				msg(QObject::tr("Only Ethernet is supported, interface %1 is not Ethernet.").arg(itstr));
				pcap_close(handle);
				break;
			}
			struct bpf_program filter;
			//Combine Filter
			QString ExFlt ;
			//判断tcp_exlude是否在0到65536之间
			if (tcp_exlude > 0 && tcp_exlude < 65536)
			{
				//如果filterStr不为空，则将filterStr和tcp_exlude组合起来
				if (filterStr.trimmed().length())
					ExFlt = "(" + filterStr.trimmed() + QString(") and not tcp port %1" ).arg(tcp_exlude);
				else
					ExFlt = QString("not tcp port %1" ).arg(tcp_exlude);
			}
			else
				ExFlt = filterStr.trimmed();
			//Compile Filter
			if (ExFlt.size())
			{
				//调用msg函数，将编译的过滤器发送出去
				msg(QObject::tr("%1 Compile Filter :%2.").arg(itstr).arg(ExFlt));
				//将ExFlt转换为std::string类型
				std::string filter_app = ExFlt.toStdString();
				bpf_u_int32 net = 0;
				//编译过滤器
				int ret32 = pcap_compile(handle, &filter, filter_app.c_str(), 0, net);
				if(ret32 < 0)
				{
					//将错误信息转换为QString类型
					QString strErrL = QString::fromLocal8Bit(QByteArray(errbuf));
					//调用msg函数，将错误信息发送出去
					msg(QObject::tr("pcap_compile return %1. %2").arg(ret32).arg(strErrL));
					pcap_close(handle);
					break;
				}
				//设置过滤器
				ret32 = pcap_setfilter(handle, &filter);
				if(ret32 < 0)
				{
					//将错误信息转换为QString类型
					QString strErrL = QString::fromLocal8Bit(QByteArray(errbuf));
					//调用msg函数，将错误信息发送出去
					msg(QObject::tr("pcap_setfilter return %1. %2").arg(ret32).arg(strErrL));
					pcap_close(handle);
					break ;
				}
			}
			const u_char *packet;
			struct pcap_pkthdr header;

			//休眠1秒
			QThread::msleep(1000);
			//调用msg函数，发送正在运行的信息
			msg(QObject::tr("RECV Running on %1").arg(itstr));
			//循环接收数据包，直到pcap_stop为true
			while (!pcap_stop)
			{
				//接收数据包
				packet = pcap_next(handle, &header);
				if(packet)
				{
					//将数据包加入队列
					if (enqueuePack((const char *)packet,header.len,id,itstr))
					{
						//每接收1000个数据包，调用msg函数，发送接收到的数据包总数
						if (++total_caps % 1000==0)
							msg(QObject::tr("%1 recv %2").arg(itstr).arg(total_caps));
					}
				}
			}
			//关闭句柄
			pcap_close(handle);
		}
		//调用msg函数，发送关闭句柄的信息
		msg(QObject::tr("RECV pcap_close for interface:%1").arg(itstr));

	}
	//3. Run Send Thread on interface
	void send_loop(QString itstr,int id, bool bSwitchMod,std::function<void (QString) > msg)
	{
		// 定义发送位置和已发送字节数
		quint64 send_pos = pcap_recv_pos;
		quint64 totalsent = 0;
		// 发送开始消息
		msg(QObject::tr("SEND Running on %1").arg(itstr));
		// 定义失败计数器
		int failedCnt = 0;
		// 循环发送数据包
		while (!pcap_stop)
		{
			// 将接口名称转换为字符串
			// 定义pcap句柄
			std::string strDev = itstr.toStdString();
			pcap_t *handle = NULL;
			// 定义错误缓冲区
			char errbuf[PCAP_ERRBUF_SIZE];
			// 清空错误缓冲区
			memset(errbuf,0,sizeof(errbuf));
			// 打开接口
			handle = pcap_open_live(strDev.c_str(), 65535, 1, 10, errbuf);
			// 如果打开失败，输出错误信息并退出循环
			if(handle == NULL)
			{
				QString strErrL = QString::fromLocal8Bit(QByteArray(errbuf));
				msg(QString("pcap_open_live failed . %1").arg(strErrL));
				break;
			}

			// 如果接口不是以太网，输出错误信息并退出循环
			if (pcap_datalink(handle)!= DLT_EN10MB)
			{
				msg(QObject::tr("SENDING Only Ethernet is supported, interface %1 is not Ethernet.").arg(itstr));
				pcap_close(handle);
				break;
			}



			// 循环发送数据包
			while (!pcap_stop)
			{
				// 如果发送位置大于接收位置，将发送位置设置为接收位置
				if (send_pos > pcap_recv_pos)
					send_pos = pcap_recv_pos;
				// 如果发送位置等于接收位置，休眠500微秒
				if (send_pos == pcap_recv_pos)
				{
					QThread::usleep(500);
					continue;
				}
				// 计算发送位置在缓冲区中的位置
				int pos = send_pos % PCAPIO_BUFCNT;
				// 发送位置加1
				++send_pos;
				// 如果发送位置加上缓冲区大小小于接收位置，将发送位置设置为接收位置，并输出警告信息
				if (send_pos + PCAPIO_BUFCNT <= pcap_recv_pos)
				{
					send_pos = pcap_recv_pos;
					msg(QObject::tr("pcap_sendpacket too slow, packet lost for :%1").arg(itstr));
				}
				// 如果数据包的发送者ID不是当前ID，并且接收者ID是当前ID或者接收者ID为-1，则发送数据包
				if (global_buffer[pos].from_id!=id &&
						((!bSwitchMod)||(global_buffer[pos].to_id==id || global_buffer[pos].to_id==-1))
						)
				{
					// 如果已发送字节数除以1000余数为0，输出已发送字节数和发送位置与接收位置的差值
					if (++totalsent % 1000==0)
					{
						msg(QObject::tr("%1 Sent %2 WATERMARK %3").arg(itstr).arg(totalsent).arg(pcap_recv_pos - send_pos));
					}
					/* Send down the packet */
					// 发送数据包
					if (pcap_sendpacket(
								handle,	// Adapter
								(const unsigned char *)global_buffer[pos].data.constData(), // buffer with the packet
								global_buffer[pos].len // size
								) ==-1)
					{
						// 如果发送失败，增加失败计数器
						if (++failedCnt >=1000)
						{
							// 如果失败计数器大于等于1000，输出错误信息并退出循环
							failedCnt = 0;
							QString strErrL = QString::fromLocal8Bit(QByteArray(errbuf));
							msg(QObject::tr("pcap_sendpacket failed %1, %2 is not connected.").arg(strErrL).arg(itstr));
							break;
						}
					}
				}
			}
			// 关闭接口
			pcap_close(handle);
		}
		// 输出关闭接口信息
		msg(QObject::tr("SEND pcap_close for interface: %1").arg(itstr));
	}

	//获取端口分配映射
	QMap<quint64,tag_portAssign> macPorts( )
	{
		mtx_ports.lock();
		QMap<quint64,tag_portAssign> ports = pcap_ports;
		mtx_ports.unlock();
		return ports;
	}

	//入队数据包
	bool enqueuePack(const  char * packet, const int len, const int npid, const QString & portname )
	{
		//判断数据包长度是否合法
		if (len>PCAPIO_MAXPACK || len <14)
			return false;
		bool enqueued = false;
		using namespace  PCAPIO;
		int dst_id = -1;
		//Src MAC
		quint64 mac_src = 0,mac_dst = 0;
		//将数据包中的源MAC地址复制到mac_src中
		memcpy_s(&mac_src,8,packet+6,6);
		//将数据包中的目的MAC地址复制到mac_dst中
		memcpy_s(&mac_dst,8,packet,6);
		bool MyPack = false;
		//获取当前时间
		QDateTime dtm = QDateTime::currentDateTime();
		mtx_ports.lock();
		const bool newClientMac = pcap_ports.contains(mac_src);
		if (!newClientMac)
		{
			tag_portAssign & newmac = pcap_ports[mac_src];
			newmac.mac = mac_src;
			newmac.portName = portname;
			newmac.curr_id = npid;
			newmac.dtmLastAck = dtm;
			MyPack = true;
		}
		else
		{
			tag_portAssign & curport = pcap_ports[mac_src];
			if (curport.curr_id==npid)
			{
				curport.dtmLastAck = dtm;
				MyPack = true;
			}
			else if	(pcap_ports[mac_src].dtmLastAck.msecsTo(dtm) >CAP_FADE_MSECS )
			{
				curport.curr_id = npid;
				curport.dtmLastAck = dtm;
				curport.portName = portname;
				MyPack = true;
			}
		}
		//Dst Mac
		const bool newDstMac = pcap_ports.contains(mac_dst);
		if (newDstMac)
		{
			if(pcap_ports[mac_dst].dtmLastAck.msecsTo(dtm) <=CAP_FADE_MSECS)
				dst_id = pcap_ports[mac_dst].curr_id;
		}
		mtx_ports.unlock();
		//Only Enqueue packs for etheraddrs connected to this port id.
		if (MyPack)
		{
			quint64 pos = pcap_recv_pos++;
			global_buffer[pos % PCAPIO_BUFCNT].from_id = npid;
			global_buffer[pos % PCAPIO_BUFCNT].to_id = dst_id;
			global_buffer[pos % PCAPIO_BUFCNT].len = len;
			memcpy_s(
						global_buffer[pos % PCAPIO_BUFCNT].data.data(),PCAPIO_MAXPACK,
					packet,len
					);
			enqueued = true;
		}
		return enqueued;
	}

}
